﻿using Fuse8.ContentManagementFramework.BizFacade.Services;
using Fuse8.ContentManagementFramework.Domain;
using Fuse8.ContentManagementFramework.Domain.Special;
using Fuse8.ContentManagementFramework.Web.Etc.PagePresent;
using Fuse8.ContentManagementFramework.Web.MvcPowered.Components.Models;
using Fuse8.DomainFramework.Common;
using Fuse8.DomainFramework.Common.Locator;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.UI;

namespace Fuse8.ContentManagementFramework.Web.MvcPowered
{
    public static class CmsPageNavigationHtmlHelperExtension
    {
        /// <summary>
        /// Renders page navigation using builder
        /// </summary>
        /// <param name="htmlHelper">Html helper</param>
        /// <param name="htmlAttributes">Html attributes that should be applied to container</param>
        /// <returns>Object of <see cref="CmsPageNavigation"/></returns>
        public static CmsPageNavigation PageNavigation(this HtmlHelper htmlHelper, object htmlAttributes = null)
        {
            return new CmsPageNavigation(htmlHelper).Attributes(htmlAttributes);
        }

        public static CmsPage CurrentContextPage(this HtmlHelper htmlHelper)
        {
            return DependencyServiceLocator.Current.GetInstance<ICmsContext>().ContextPage;
        }

        [Obsolete("Use GetPageByPageKey method")]
        public static CmsPage GetPageByPublicId(this HtmlHelper htmlHelper, int pageId)
        {
            return new CmsPageService().FindByPublicId(pageId);
        }

        public static CmsPage GetPageByPageKey(this HtmlHelper htmlHelper, string pageKey)
        {
            return new CmsPageService().FindByPageKey(pageKey);
        }

        public static CmsPage GetParentPage(this HtmlHelper htmlHelper, int pageId, int level)
        {
            var data = GetParentPages(htmlHelper, pageId);

            return data.FirstOrDefault(p => p.PageLevel == level);
        }

        public static IEnumerable<CmsPage> GetParentPages(this HtmlHelper htmlHelper, int pageId)
        {
            return new CmsPageService().GetParentsPages(pageId);
        }
    }

    public class CmsPageComponentItemForNavigation : CmsPageComponentItem
    {
        public CmsPageNavigation ParentControl { get; set; }

        public bool CancelResetCurrentState { get; set; }

        public CmsPageComponentItemForNavigation()
            : base()
        { }

        public CmsPageComponentItemForNavigation(CmsPage page)
            : base(page)
        {
        }

        public CmsPageComponentItemForNavigation(CmsPageComponentItem adaptie)
        {
            Guard.ArgumentNotNull(adaptie, "adaptie");

            PageId = adaptie.PageId;
            Title = adaptie.Title;
            Url = adaptie.Url;
            IsLatest = adaptie.IsLatest;
            IsCurrent = adaptie.IsCurrent;
            IsVisible = adaptie.IsVisible;
            Level = adaptie.Level;
            ChildPages = adaptie.ChildPages;
            PublicPageId = adaptie.PublicPageId;
            PageBehaviourType = adaptie.PageBehaviourType;
            IsOpenInNewWindow = adaptie.IsOpenInNewWindow;
        }
    }

    /// <summary>
    /// Page navigaiton builder
    /// </summary>
    /// <remarks>By default renders &lt;ul&gt; element with class "b-nav".</remarks>
    public class CmsPageNavigation : CmsMvcBaseComponent<CmsPageNavigation, IEnumerable<CmsPageComponentItemForNavigation>>
    {
        #region Fields

        private CmsPage _targetPage;
        private CmsPage _currentPage;
        private int _maxLevel;
        private int _maxVisibleLevel;
        private bool _includeRoot = true;
        private readonly CmsPageService _pageService = new CmsPageService();

        #endregion Fields

        #region Fields

        protected Func<IEnumerable<CmsPageComponentItem>, object> ItemTemplate { get; set; }

        protected virtual CmsPage TargetPage
        {
            get
            {
                if (_targetPage == null)
                {
                    _targetPage = _pageService.RootPage;
                }

                return _targetPage;
            }
            set
            {
                _targetPage = value;
            }
        }

        protected virtual CmsPage CurrentPage
        {
            get
            {
                if (_currentPage == null)
                {
                    _currentPage = DependencyServiceLocator.Current.GetInstance<ICmsContext>().ContextPage;
                }

                return _currentPage;
            }
        }

        protected override IEnumerable<CmsPageComponentItemForNavigation> Value
        {
            get
            {
                var value = base.Value;

                var currentPage = CurrentPage;

                if (TargetPage != null && value == null)
                {
                    var data = _pageService.GetChildPages(TargetPage.Id, _includeRoot, _maxLevel > 0 ? _maxLevel : new Nullable<int>());

                    if (data != null)
                    {
                        data = data.Where(p => p.IsVisible);
                        var strategy = PagePresentStrategyFactory.GetStrategy(TargetPage);

                        var list = new List<CmsPageComponentItemForNavigation>();

                        foreach (var item in strategy.GetNavigationItems())
                        {
                            item.CancelResetCurrentState = currentPage == null;
                            list.AddRange(item.GetHierarchyFromCollection(data, currentPage, _maxVisibleLevel));
                        }

                        value = list.Select(
                            p => new CmsPageComponentItemForNavigation(p)
                                     {
                                         ParentControl = this
                                     });

                        if (_maxVisibleLevel > 0)
                        {
                            value = value.Where(p => p.IsVisible);
                        }

                        base.Value = value;
                    }
                }

                return value;
            }
        }

        #endregion Fields

        internal CmsPageNavigation(HtmlHelper htmlHelper, CmsPageService service, CmsPage currentPage)
            : this(htmlHelper)
        {
            _pageService = service;
            _currentPage = currentPage;
        }

        public CmsPageNavigation(HtmlHelper htmlHelper)
            : base(htmlHelper)
        {
            Component = this;
            SetDefaultChildTemplate();
            SetDefaultMainTemplate();
        }

        private void SetDefaultMainTemplate()
        {
            Wrapper(tag: "ul", htmlAttributes: new { @class = "b-nav" });
            InlineTemplate = p =>
                                 {
                                     var result = new StringBuilder();
                                     foreach (var item in p)
                                     {
                                         result.AppendFormat(
                                             "<li class=\"b-page-{0} b-nav__item {1}\"{4}><a href=\"{2}\">{3}</a></li>",
                                             item.PublicPageId,
                                             item.IsCurrent ? "b-nav__item_current" : "", item.Url, item.Title,
                                             item.IsOpenInNewWindow ? " target=\"_blank\"" : "");
                                         // do not render Root page children as in most cases they should be rendered in same level
                                         if (item.HasChildren && item.Level > 0)
                                         {
                                             result.AppendFormat(
                                                 Component.RenderItemTemplate(item.ChildPages).ToHtmlString());
                                         }
                                     }
                                     return result.ToString();
                                 };
        }

        private void SetDefaultChildTemplate()
        {
            ItemTemplate = p =>
                               {
                                   var result = new StringBuilder();
                                   result.Append("<ul class=\"b-nav__sub-nav\">");
                                   foreach (var item in p)
                                   {
                                       result.AppendFormat(
                                           "<li class=\"b-page-{0} b-nav__item {1}\" {4}><a href=\"{2}\">{3}</a></li>",
                                           item.PublicPageId,
                                           item.IsCurrent ? "b-nav__item_current" : "", item.Url, item.Title,
                                           item.IsOpenInNewWindow ? "target=\"_blank\"" : "");
                                   }
                                   result.Append("</ul>");
                                   return result.ToString();
                               };
        }

        /// <summary>
        /// Sets start page for navigation
        /// </summary>
        /// <param name="page">Page</param>
        /// <returns></returns>
        public CmsPageNavigation ForPage(CmsPage page)
        {
            Guard.ArgumentNotNull(page, "page");

            TargetPage = page;

            return this;
        }

        /// <summary>
        /// Sets max level of deep to retriew child pages
        /// </summary>
        /// <param name="level"></param>
        /// <returns></returns>
        public CmsPageNavigation MaxLevel(int level)
        {
            if (level > 0)
            {
                _maxLevel = level;
            }

            return this;
        }

        /// <summary>
        /// Sets max level of
        /// </summary>
        /// <param name="level"></param>
        /// <returns></returns>
        public CmsPageNavigation MaxVisibleLevel(int level)
        {
            if (level > 0)
            {
                _maxVisibleLevel = level;
            }

            return this;
        }

        /// <summary>
        /// Indicates whether it is necessary to exclude page spcified in ForPage method
        /// </summary>
        /// <param name="excludeRoot"></param>
        /// <returns></returns>
        public CmsPageNavigation ExcludeRoot(bool excludeRoot)
        {
            _includeRoot = !excludeRoot;

            return this;
        }

        /// <summary>
        /// Sets HTML template for child pages processing
        /// </summary>
        /// <param name="template">Html template</param>
        /// <returns></returns>
        public CmsPageNavigation ChildPagesTemplate(Func<IEnumerable<CmsPageComponentItem>, object> template)
        {
            Guard.ArgumentNotNull(template, "template");

            if (template != null)
            {
                ItemTemplate = template;
            }

            return this;
        }

        /// <summary>
        /// Renders specified pages using template specified in method ChildPagesTemplate
        /// </summary>
        /// <param name="items">Collection of pages</param>
        /// <returns></returns>
        public MvcHtmlString RenderItemTemplate(IEnumerable<CmsPageComponentItem> items)
        {
            using (var output = new StringWriter())
            {
                if ((items != null) && (ItemTemplate != null))
                {
                    using (HtmlTextWriter textWriter = new HtmlTextWriter(output))
                    {
                        textWriter.Write(ItemTemplate(items).ToString());
                    }
                }

                return MvcHtmlString.Create(output.ToString());
            }
        }
    }
}